﻿/**
 * 驱动程序主模块
 *
 * 此模块定义了驱动程序的入口回调函数和相关管理接口。
 */

#include <ntddk.h>
#include <wdf.h>
#include <initguid.h>

#include "trace.h"
#include "public.h"
#include "device.h"
#include "driver.tmh"

//
// WDFDRIVER Events
//
DRIVER_INITIALIZE              DriverEntry;
static EVT_WDF_DRIVER_DEVICE_ADD      zyhelloEvtDeviceAdd;
static EVT_WDF_OBJECT_CONTEXT_CLEANUP zyhelloEvtDriverContextCleanup;

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, zyhelloEvtDeviceAdd)
#pragma alloc_text(PAGE, zyhelloEvtDriverContextCleanup)
#endif

/**
DriverEntry initializes the driver and is the first routine called by the
system after the driver is loaded. DriverEntry specifies the other entry
points in the function driver, such as EvtDevice and DriverUnload.

@param DriverObject - represents the instance of the function driver that is loaded
    into memory. DriverEntry must initialize members of DriverObject before it
    returns to the caller. DriverObject is allocated by the system before the
    driver is loaded, and it is released by the system after the system unloads
    the function driver from memory.
@param RegistryPath - represents the driver specific path in the Registry.
    The function driver can use the path to store driver related data between
    reboots. The path does not store hardware instance specific data.
@return NTSTATUS
@retval STATUS_SUCCESS if successful,
@retval STATUS_UNSUCCESSFUL otherwise.
*/
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
    NTSTATUS              status;
    WDF_DRIVER_CONFIG     config;
    WDF_OBJECT_ATTRIBUTES attributes;

    // Initialize WPP Tracing
    WPP_INIT_TRACING(DriverObject, RegistryPath);
    TraceInfo(DBG_INIT, "%!FUNC! Entry");

    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    WDF_DRIVER_CONFIG_INIT(&config, zyhelloEvtDeviceAdd);

    // Register a cleanup callback so that we can call WPP_CLEANUP when the framework driver object is deleted during driver unload.
    attributes.EvtCleanupCallback = zyhelloEvtDriverContextCleanup;

    status = WdfDriverCreate(DriverObject, RegistryPath, &attributes, &config, WDF_NO_HANDLE);
    if(!NT_SUCCESS(status)) {
        TraceError(DBG_INIT, "WdfDriverCreate failed %!STATUS!", status);
        WPP_CLEANUP(DriverObject);
        return status;
    }

    TraceInfo(DBG_INIT, "%!FUNC! Exit");
    return status;
}

/**
EvtDeviceAdd is called by the framework in response to AddDevice call from the PnP manager. 
We create and initialize a device object to represent a new instance of the device.

@param Driver - Handle to a framework driver object created in DriverEntry
@param DeviceInit - Pointer to a framework-allocated opaque WDFDEVICE_INIT structure.
    Memory for this structure will be freed by the framework when the WdfDeviceCreate
    succeeds. So don't access the structure after that point.
@return NTSTATUS
*/
static NTSTATUS zyhelloEvtDeviceAdd(_In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit) {
    NTSTATUS              status;
    WDF_OBJECT_ATTRIBUTES deviceAttributes;
    PDEVICE_CONTEXT       deviceContext;
    WDFDEVICE             device;

    UNREFERENCED_PARAMETER(Driver);
    PAGED_CODE();
    TraceInfo(DBG_INIT, "%!FUNC! Entry");

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);

    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
    if(!NT_SUCCESS(status)) {
        TraceError(DBG_INIT, "WdfDeviceCreate failed: %!STATUS!", status);
        return status;
    }

    //
    // Get a pointer to the device context structure that we just associated
    // with the device object. We define this structure in the device.h
    // header file. DeviceGetContext is an inline function generated by
    // using the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro in device.h.
    // This function will do the type checking and return the device context.
    // If you pass a wrong object handle it will return NULL and assert if
    // run under framework verifier mode.
    //
    deviceContext = DeviceGetContext(device);

    //
    // Initialize the context.
    //
    deviceContext->readTimeoutMs = 1000;
    strncpy(deviceContext->demoBuf, "This is from zyhelloEvtIoRead().", sizeof(deviceContext->demoBuf));

    //
    // Create a device interface so that applications can find and talk to us.
    //
    status = WdfDeviceCreateDeviceInterface(device, &GUID_DEVINTERFACE_zyhello, NULL);
    if(!NT_SUCCESS(status)) {
        TraceError(DBG_INIT, "WdfDeviceCreateDeviceInterface failed %!STATUS!", status);
        return status;
    }

    //
    // Initialize the I/O Package and any Queues
    //

    // A single default I/O Queue is configured for parallel request
    // processing, and a driver context memory allocation is created
    // to hold our structure QUEUE_CONTEXT.
    //
    // Configure a default queue so that requests that are not
    // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto
    // other queues get dispatched here.
    //
    WDF_IO_QUEUE_CONFIG queueConfig;
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel);
    queueConfig.EvtIoDeviceControl = zyhelloEvtIoDeviceControl;
    queueConfig.EvtIoStop          = zyhelloEvtIoStop;
    queueConfig.EvtIoRead          = zyhelloEvtIoRead;  // callback handler for read requests
    queueConfig.EvtIoWrite         = zyhelloEvtIoWrite;  // callback handler for write requests

    WDFQUEUE queue;
    status = WdfIoQueueCreate(device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue);
    if(!NT_SUCCESS(status)) {
        TraceError(DBG_INIT, "WdfIoQueueCreate failed %!STATUS!", status);
        return status;
    }

    TraceInfo(DBG_INIT, "%!FUNC! Exit");
    return status;
}

/**
Free all the resources allocated in DriverEntry.
@param DriverObject - handle to a WDF Driver object.
@return VOID
*/
static VOID zyhelloEvtDriverContextCleanup(_In_ WDFOBJECT DriverObject) {
    UNREFERENCED_PARAMETER(DriverObject);
    PAGED_CODE();
    TraceInfo(DBG_INIT, "%!FUNC! Entry");
    // Stop WPP Tracing
    WPP_CLEANUP(WdfDriverWdmGetDriverObject((WDFDRIVER)DriverObject));
}
